/*
*    l3qd - Light, light, lightweight queue
*    Copyright (C) 2024  Marcus Pedersén marcus@marcux.org
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package cert

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
	"path/filepath"
	"time"
)

// Creates self signed certificates
// and print them to files into
// certDir directory.
func GenerateCert(certDir string) error {
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to generate private key: %s", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(10 * 365 * 24 * time.Hour)

	serialNumber, err := rand.Int(rand.Reader,
		new(big.Int).Lsh(big.NewInt(1), 256))
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"l3q"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,
		KeyUsage: x509.KeyUsageKeyEncipherment |
			x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader,
		&template, &template, &priv.PublicKey, priv)
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to create certificate: %s", err)
	}

	certOut, err := os.Create(filepath.Join(certDir, "cert.pem"))
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to create file: %s, %s",
			filepath.Join(certDir, "cert.pem"), err)
	}

	pem.Encode(certOut,
		&pem.Block{
			Type:  "CERTIFICATE",
			Bytes: derBytes,
		})
	certOut.Close()

	keyOut, err := os.Create(filepath.Join(certDir, "key.pem"))
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to create file: %s, %s",
			filepath.Join(certDir, "key.pem"), err)
	}

	bytesKey, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return fmt.Errorf("Generate certificate: Failed to parse private key: %s", err)
	}

	pem.Encode(keyOut, &pem.Block{
		Type:  "EC PRIVATE KEY",
		Bytes: bytesKey,
	})
	keyOut.Close()

	if err := os.Chmod(filepath.Join(certDir, "key.pem"), 0600); err != nil {
		return fmt.Errorf("Generate certificate: Failed to change permission on file: %s: %s",
			filepath.Join(certDir, "key.pem"), err)
	}

	return nil
}
